package main

import "fmt"

// ==========函数==========
// 1、函数的形参可以是多个, 返回值也可以是多个
// 2、形参和返回值的数据类型可以是值类型也可以是引用类型
// 3、函数的命名遵循标识符命名规范, 首字母不能是数字
// 4、首字母大写的函数可以被本包文件和其他包文件使用, 首字母小写的函数只能被本包使用
// 5、函数中的变量是局部的, 函数外不生效
// 6、基本数据类型和数组是值传递, 即进行值拷贝, 在函数内修改不会影响到原来的值
// 7、如果希望函数内的变量能够修改函数外的变量, 可以传入变量的地址, 函数内以指针的方式操作变量
// 8、golang中函数不支持重载(函数名相同, 参数不同)
// 9、函数是一种数据类型, 因此在golang中, 函数可以作为形参, 并且调用
// 10、golang支持自定义数据类型
// 11、支持对函数返回值命名
// 12、golang支持可变参数
// 13、函数参数的传递方式包括: 值传递、引用传递
//     不管是值传递还是引用传递, 传递给函数的都是变量的副本, 不同的是, 值传递的是值的拷贝, 引用传递的是地址的拷贝
//     一般来说, 地址拷贝效率高, 因为数据量小, 而值拷贝决定拷贝的数据大小, 数据越大效率越低

// 递归函数
func recursion(n int) {
	if n > 2 {
		n--
		recursion(n)
	}
}

// 斐波那契数列
func fibonacci(n int) int {
	if n == 1 || n == 2 {
		return 1
	}
	return fibonacci(n-1) + fibonacci(n-2)
}

// 通过传入指针在函数内修改函数外部的值
func pointerModifyVar(p *int) {
	*p = 1
}

func numSub(n1, n2 int) int {
	return n1 - n2
}

// 函数作为参数传入
func callFunction(funcVar func(int, int) int, n1, n2 int) int {
	return funcVar(n1, n2)
}

// 自定义一个函数数据类型, 将func(int, int) int简写为customFunc
type customFunc func(int, int) int

// 给int取别名, 在golang中myInt和int虽然都是int类型, 但是golang认为它们还是两个类型, 需要强制转换
type mimeInt int

func callCustom(c customFunc, n1, n2 int) int {
	return c(n1, n2)
}

// 对函数返回值命名
func renameFun(n1, n2 int) (sum, sub int) {
	sum = n1 + n2
	sub = n1 - n2
	// 这样的话就不需要在return后加sum和sub, 直接return就好
	return
}

// 可变参数, 至少有一个变量n1, args就是一个int类型的切片
func sumFunc(n1 int, args ...int) (res int) {
	res = n1
	for i := 0; i < len(args); i++ {
		res += args[i] // 切片通过索引取出数据
	}
	return
}

// 匿名函数
func hiddenNameFunc() int {
	return func(a int) int { return a }(1)
}

// 作用域规则
func scope() {
	// 1、函数内的变量只能在函数scope内部使用, 不能在scope函数外使用变量

	// 2、for循环中的变量i只在for代码块有效, 不能在for代码块外面使用变量i
	for i := 0; i < 5; i++ {
	}
	// fmt.Println(i)  错误

	// 3、if中的变量j只在if代码块有效, 不能在if代码块外面使用变量j
	if j := 0; j < 1 {
	}
	// fmt.Println(j)  错误
}

var name1 = "allen"
var name2 = "max"

func callName() { // 就近原则
	name1 = "jason"     // jason直接赋给了全局变量name1, 因此全局变量name1="allen"变成了"jason"
	name2 := "jonathon" // jonathon赋给了局部变量name2, 不影响全局变量name2对应的max
	fmt.Println("callName() name1 =", name1)
	fmt.Println("callName() name2 =", name2)
}

func main() {
	recursion(4) // 2 2 3
	fibonacci(10)
	var i int
	pointerModifyVar(&i)
	callFunction(numSub, 1, 2)
	callCustom(numSub, 1, 2)
	renameFun(1, 2)
	sumFunc(1, 2, 3)
	hiddenNameFunc()
	scope()
	callName()
}
